/*  
 *  ChessGame.cs 
 *  Copyright(c) 2007 Adam W Adair
 *
 *  This file is part of Adam's Chess Engine (ACE).
 *
 *  ACE is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *   
 *  ACE is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with ACE; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
using System;
using System.Collections.Generic;
using System.Text;
using ACE.Engine;

namespace ACE.Board
{
    /// <summary>
    /// Manages the chess game.
    /// </summary>
    class ChessGame
    {
        public const int WHITE = 0;
        public const int BLACK = 1;
        public const int TIE=3;
        public const int NOT_OVER=-1;
        public const int COMPUTER_OFF = -1;

        public enum GameStatus {WHITE_MATE, BLACK_MATE, STALEMATE, REPETITION_DRAW, FIFTY_MOVE_DRAW, PLAYING};
        
        private int m_ComputerSide;
        private ACEEngine m_engine;

        ChessBoard m_board;

        /// <summary>
        /// Status of the game
        /// </summary>
        public GameStatus Status
        {
            get
            {
                bool movable = m_board.LegalMoveExists();
                if (!movable)
                {
                    if (m_board.IsMovingSideChecked())
                    {
                        if (m_board.OtherSide == ChessBoard.WHITE)
                            return GameStatus.WHITE_MATE;
                        else
                            return GameStatus.BLACK_MATE;
                    }
                    else
                        return GameStatus.STALEMATE;
                }
                if (m_board.NoCaptureMoves >= 100)
                {
                    return GameStatus.FIFTY_MOVE_DRAW;
                }
                return GameStatus.PLAYING;

            }
        }

        public ChessGame()
        {
            m_board = new ChessBoard();
            m_ComputerSide = COMPUTER_OFF;
            
        }

        public void Undo()
        {
            m_board.undo();
            TurnOffComputerPlayer();
        }

        public ChessBoard Board
        {
            get
            {
                return m_board;
            }
        }

        public void init()
        {
            m_board.SetupInitialPosition();
        }

        /// <summary>
        /// Signals the computer to take over playing the side that currently needs
        /// to move.
        /// </summary>
        public void TurnOnComputerPlayer()
        {
            m_ComputerSide = Board.SideToMove;
            m_engine = new ACEEngine();

            // Imediately find the next move
        }

        public void TurnOffComputerPlayer()
        {
            m_ComputerSide = COMPUTER_OFF;
        }

        public bool IsComputerTurn()
        {
            return (m_ComputerSide == Board.SideToMove);
        }

        public void FEN_Position(string FEN)
        {
            m_board = new ChessBoard(FEN);

        }

        public void TryMove(string moveString)
        {
            Move move = findMove(moveString);
            if (move == null) throw new ACEException("Invalid move.");
            if (m_board.MakeMove(move))
            {
                m_board.GeneratePotentialMoves();
            }
            else
            {
                throw new Exception("Invalid move.");
            }

        }

        public Move ComputerPlay()
        {
            Move move = m_engine.GetNextMove(m_board);
            if (m_board.MakeMove(move))
            {
                m_board.GeneratePotentialMoves();
                return move;
            }
            return null;
            
        }

        /// <summary>
        /// attempts to find the move in the list of valid moves.
        /// </summary>
        /// <param name="moveString">the move command string</param>
        /// <returns>The move</returns>
        private Move findMove(string moveString)
        {
            char[] s = moveString.ToLower().ToCharArray();
            char pc = (char)0;
            Piece pp = null;
            if (s[0] < 'a' || s[0] > 'h' ||
                s[1] < '0' || s[1] > '9' ||
                s[2] < 'a' || s[2] > 'h' ||
                s[3] < '0' || s[3] > '9')
                return null;

            if (s.Length > 4)
            {
                pc = s[4];
                pp = getPromotionPiece(pc);
            }

            int from = s[0] - 'a';
            from += 8 * (8 - (s[1] - '0'));

            int to = s[2] - 'a';
            to += 8 * (8 - (s[3] - '0'));

            List<Move> mvs = m_board.PotentialMoves;
            for (int i = 0; i < mvs.Count; ++i)
            {
                if (mvs[i].To == to && mvs[i].From == from)
                {
                    if (mvs[i].IsPromotion)
                    {
                        if (pp == null) pp = getPromotionPiece('q');
                        if (mvs[i].PromotionPiece == pp)
                            return mvs[i];
                    }
                    else
                    {
                        return mvs[i];
                    }
                }
            }
            return null;
        }

        private Piece getPromotionPiece(char pc)
        {
            switch(pc)
            {
                case 'n':
                    return ( m_board.SideToMove == WHITE ) ? Knight.WhiteKnight : Knight.BlackKnight;
                case 'b':
                    return ( m_board.SideToMove == WHITE ) ? Bishop.WhiteBishop : Bishop.BlackBishop;
                case 'r':
                    return ( m_board.SideToMove == WHITE ) ? Rook.WhiteRook : Rook.BlackRook;
                case 'q':
                    return ( m_board.SideToMove == WHITE ) ? Queen.WhiteQueen : Queen.BlackQueen;
                default:
                    return null;
            }
        }
    }
}
